#include "udp_sever.hpp"
#include "Users.hpp"
#include <memory>
#include <fstream>
#include <unordered_map>
#include <signal.h>
using namespace Users;
// ./udp_sever.exe port

static std::string file = "dict.txt";
static std::unordered_map<std::string,std::string> dict;

static bool stringSubstr(const std::string& line,std::string& key,std::string& value,const char find)
{
    auto pos = line.find(find);
    if(pos == std::string::npos)
    {
        std::cout << "没有收入该词" << std::endl;
        return false;
    }
    //成功找到开始，分割
    key = line.substr(0,pos); //[)
    value = line.substr(pos+1); //[)
    return true;
}

void initDict()
{
    std::ifstream in(file,std::ios::binary);
    if(!in.is_open())
    {
        std::cout << errno << strerror(errno) << std::endl;
        exit(4);
    }

    char find = ':';
    std::string line;
    std::string key,value;
    //getline？
    while(getline(in,line))
    {
         if(stringSubstr(line,key,value,find))
             dict.insert(make_pair(key,value));
    }

    in.close();
}

void handler(int signo)
{
    //如果收到了二号信号，改为热加载新词典
    initDict();
    std::cout << "词典已经刷新" << std::endl;
}


static void Uages()
{
    std::cout <<"Uages. tip :" << "./udp_sever.exe  severport" << std::endl;
}

//demo1
void dictText(const int sockfd,std::string consumerip,uint16_t consumerport,std::string message)
{
    // 从这里开始，执行我们的任务，这样执行任务与服务端收到服务之间就进行了解耦
    //利用hash的键值对建立哈希表
    initDict();
    std::unordered_map<std::string,std::string>::iterator it = dict.find(message);
    if(it == dict.end())
    {
        std::cout << "没有收入该词,但是你可以按下ctrl+c，热加载你想加入的词" << std::endl;
        return;
    }
    //
    //std::cout << message << "->" << it->second << std::endl;
    //发客户端翻译的内容
    //const char* buffer;
    std::string send = it->second;
    //buffer = send.c_str();
   
    struct  sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(consumerport);
    address.sin_addr.s_addr = inet_addr(consumerip.c_str());

    sendto(sockfd,send.c_str(),send.size(),0,(struct sockaddr*)&address,sizeof(address));
}    
//demo2 使用popen实现 管道+fork+进程切换三位一体的shell命令外壳
void shell(const int sockfd,std::string consumerip,uint16_t consumerport,std::string message)
{
 
     FILE* file = popen(message.c_str() , "r");
     std::string send("\n");
     if(file == nullptr)
     {
        std::cout << errno <<  "命令行解析有误" << std::endl;
        exit(5);
     }

     char  buffer[1024];
     //按行去读取
     while(fgets(buffer,sizeof buffer,file))
     send += buffer;
     //读取完成后，发送给客户端
     struct  sockaddr_in address;
     bzero(&address,sizeof(address));
     address.sin_family = AF_INET;
     address.sin_port = htons(consumerport);
     address.sin_addr.s_addr = inet_addr(consumerip.c_str());
 
     sendto(sockfd,send.c_str(),send.size(),0,(struct sockaddr*)&address,sizeof(address));

     fclose(file);
}

onLineUser chatusers;
void chatroom(const int sockfd,std::string consumerip,uint16_t consumerport,std::string message)
{

     //申请上线/下线
     if(message == "online") chatusers.addUser(consumerip,consumerport);
     if(message == "offline") chatusers.delUser(consumerip,consumerport);

     //先检查一下是否上限了
     if(!chatusers.is_online(consumerip,consumerport))
     {
        //单独给该客户端发送，你还没有上限;
        std::string send = "你还没有上线,可以尝试输入online上线";

        struct  sockaddr_in address;
        bzero(&address,sizeof(address));
        address.sin_family = AF_INET;
        address.sin_port = htons(consumerport);
        address.sin_addr.s_addr = inet_addr(consumerip.c_str());
    
        sendto(sockfd,send.c_str(),send.size(),0,(struct sockaddr*)&address,sizeof(address));
     }
     else
     {
        //到这里了一定是上线的，刚上线的也会给所有人群发消息表示自己上线了
        chatusers.boreacast(sockfd,consumerip,consumerport,message);
     }
}

int main(int argc,char * argv[])
{
     if(argc != 2)
     {
        Uages();
        exit(4);
     }

    uint16_t port = atoi(argv[1]);

    std::unique_ptr<Server> udpServer(new Server(chatroom,port,"0.0.0.0"));

    signal(2,handler);

    udpServer->initServer();
    udpServer->start();
    //udpServer->join();
    return 0;
}